package com.gmail.dengtao.joe.commons.crypto.cipher;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

/**
 * Encrypt/decrypt input using the Blowfish Cipher.
 * @author <a href="mailto:joe.dengtao@gmail.com">DengTao</a>
 * @version 1.0
 * @since 1.0
 */
public class Blowfish {

    private String transformation = CipherUtils.BLOWFISH;
	
    public Blowfish(String transformation) {
    	this.transformation = transformation;
    }
    
    public Blowfish(String mode, String padding) {
    	transformation = CipherUtils.BLOWFISH + "/" + mode + "/" + padding;
    }

	public byte[] encrypt(byte[] data, byte[] key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        return process(data, key, Cipher.ENCRYPT_MODE);
    }

    public byte[] decrypt(byte[] data, byte[] key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        return process(data, key, Cipher.DECRYPT_MODE);
    }

    private byte[] process(byte[] data, byte[] key, int type) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
    	if (type == Cipher.ENCRYPT_MODE && data.length % 8 != 0) {
    		byte[] tmp = new byte[(data.length / 8 + 1 ) * 8];
    		for (int i = 0; i < tmp.length; i++) {
    			tmp[i] = 0;
			}
    		System.arraycopy(data, 0, tmp, 0, data.length);
    		data = tmp;
    	}
    	//System.out.println("O:" + HexUtils.bytesToHex(data));
    	
    	SecretKeySpec secretKeySpec = new SecretKeySpec(key, CipherUtils.BLOWFISH);
        Cipher cipher = CipherUtils.getCipher(transformation);
        cipher.init(type, secretKeySpec);
        
        InputStream is = new ByteArrayInputStream(data);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
			byte[] buf = new byte[8];
			int buf_len;
			while ((buf_len = is.read(buf)) != -1) {
				byte[] block = null;
				if (buf.length == buf_len) {
					block = buf;
				} else {
					block = new byte[buf_len];
					for (int i = 0; i < buf_len; i++) {
						block[i] = buf[i];
					}
				}
		    	//System.out.println("P:" + HexUtils.bytesToHex(block));
		    	byte[] tm = cipher.doFinal(block);
				bos.write(tm);
		    	///System.out.println("E:" + HexUtils.bytesToHex(tm));

			}
			return bos.toByteArray();
        } catch (IOException e) {
        	e.printStackTrace();
        	return null;
		} finally {
			if (bos != null) try { bos.close(); } catch (IOException e) { }
			if (is != null) try { is.close(); } catch (IOException e) { }
		}
    }

    //private byte[] subkey(, key) {
    	
    //}
    
}